package context.support;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.alibaba.fastjson.JSON;

import beans.factory.annotation.Autowired;
import beans.factory.annotation.Value;
import beans.factory.support.GenericBeanDefinition;
import context.context.ApplicationContext;
import context.stereotype.Component;
import context.stereotype.Repository;
import context.stereotype.Service;
import context.util.ClassTools;

public class ClassPathXmlApplicationContext implements ApplicationContext{
	Logger log = Logger.getLogger(ClassPathXmlApplicationContext.class);
	
	List<GenericBeanDefinition> beanList = new ArrayList<GenericBeanDefinition>();
	
	Map<String, HashMap<String, String>> childElement = new HashMap<String, HashMap<String, String>>();
	
	Map<String, Object> sigletions = new HashMap<String, Object>();
	
	public ClassPathXmlApplicationContext(String fileName) {
		//读取配置文件中管理的bean
        this.readXML(fileName);
        //实例化bean
        this.instancesBean();
        //注解处理器
        this.annotationInject();
	}
	
	/**
	 * @Title: readXML
	 * @Description: 解析配置文件
	 * @param fileName
	 * @date 2021/04/29
	 */ 
	public void readXML(String fileName) {
		Document document = null;
		SAXReader saxReader = new SAXReader();
		try {
			ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
			document = saxReader.read(classLoader.getResourceAsStream(fileName));
			Element beans = document.getRootElement();
			for (Iterator<Element> beansList = beans.elementIterator();beansList.hasNext();) {
				Element element = beansList.next();
				System.out.println(element.getName());
				// TODO 这里使用策略模式怎么样？
				if(Objects.equals("bean", element.getName())) {
					GenericBeanDefinition bean = new GenericBeanDefinition(element.attributeValue("id"), element.attributeValue("class"));
					beanList.add(bean);
					List<Element> sons = element.elements();
					HashMap<String, String> perperties = new HashMap<String, String>();
					for(int i=0; i<sons.size(); i++) {
						Element son = sons.get(i);
						String name = son.attributeValue("name");
						String value = son.attributeValue("value");
						perperties.put(name, value);
					}
					childElement.put(element.attributeValue("id"), perperties);
				}else if(Objects.equals("alias", element.getName())) {
					
				}else if(Objects.equals("import", element.getName())) {
					
				}else if(Objects.equals("description", element.getName())) {
					
				}else if(Objects.equals("component-scan", element.getName())) { // <context:component-scan />
					String value = element.attributeValue("base-package");
					Set<Class<?>> classes = ClassTools.getClasses(value);
					Iterator<Class<?>> classesIterator = classes.iterator();
					while(classesIterator.hasNext()){
						Class className = classesIterator.next();
						try {
							if(this.isTypeAnnotion(className)){
								String classNameSuffix = className.getName().split("\\.")[className.getName().split("\\.").length-1];
								StringBuilder id = new StringBuilder(classNameSuffix);
								// 如果首字母是大写，则转换为小写。如UserDaoImple转为userDaoImple
								char[] chars = new char[1];
						        chars[0] = id.charAt(0);
						        String temp = new String(chars);
						        if (chars[0] >= 'A' && chars[0] <= 'Z') {
						            id = new StringBuilder(classNameSuffix.replaceFirst(temp, temp.toLowerCase()));
						        }
								GenericBeanDefinition bean = new GenericBeanDefinition(id.toString(), className.getName());
								beanList.add(bean);
							}
						} catch (ClassNotFoundException e) {
							e.printStackTrace();
						}
					}
					
				}else if(Objects.equals("config", element.getName())) { // <aop:config />
					
				}else if(Objects.equals("advice", element.getName())) { // <tx:advice />
					
				}else if(Objects.equals("annotation-driven", element.getName())) { // <tx:annotation-driven />
					
				}else if(Objects.equals("jta-transaction-manager", element.getName())) { // jta-transaction-manager 
					
				}else {
					log.info(element.getName()+"标签，待处理");
				}
			
			}
			System.out.println("bean集合："+JSON.toJSONString(beanList));
			
		}catch (DocumentException e){
			log.info("解析配置文件出错......"+e.getMessage());
		}
	}
	
	/**
	 * @Title: instancesBean
	 * @Description: 实例化Bean
	 * @date 2021/04/29
	 */ 
	public void instancesBean() {
		for(GenericBeanDefinition bean : beanList) {
			try {
				Object instance = Class.forName(bean.getClassName()).newInstance();
				
				// 如果bean标签中含有子标签，会在此赋值
				if(!Objects.equals(null, childElement.get(bean.getId()))) {
					HashMap<String, String> properties = childElement.get(bean.getId());
					for(String key : properties.keySet()) {
						Field field = instance.getClass().getDeclaredField(key);
						//Class.forName(bean.getClassName());
						Class type = field.getType();
						field.setAccessible(true);
						if(Objects.equals("id", field.getName())) {
							field.set(instance, Integer.valueOf(properties.get(key)));
						}else {
							field.set(instance, properties.get(key));
						}
						
					}
				}
				//sigletions.put(bean.getId(), Class.forName(bean.getClassName()).newInstance());
				sigletions.put(bean.getId(), instance);
			} catch (Exception e) {
				log.info("实例化出错......"+e.getMessage());
			}
		}
		System.out.println("实例化的对象："+JSON.toJSONString(sigletions));
	}
	
	/** 
     * 注解处理器 
     * 如果注解配置了属性，则根据属性所指定的名称获取要注入的实例引用， 
     * 如果注解没有配置属性，则根据属性所属类型来扫描配置文件获取要注入的实例引用 
     *  
     */
	public void annotationInject() {
		for(String beanName : sigletions.keySet()) {
			Object bean = sigletions.get(beanName);
			if(bean != null) {
				this.fieldAnnotation(bean);
				this.propertyAnnotion(bean);
			}
		}
	}
	
	/**
	 * @Title: typeAnnotion
	 * @Description: 处理在类上的注解
	 * @param packName
	 * @throws ClassNotFoundException
	 */ 
	public boolean isTypeAnnotion(Class className) throws ClassNotFoundException {
		Component component = (Component) className.getAnnotation(Component.class);
		Repository repository = (Repository) className.getAnnotation(Repository.class);
		Service service = (Service) className.getAnnotation(Service.class);
		
		if(!Objects.equals(component, null) || !Objects.equals(repository, null) || !Objects.equals(service, null)) {
			return true;
		}
		return false;
		
	}
	
	/** 
	 * 处理在字段上的注解 
	 * 能放在字段上的注解都需要在此处理
	 * @param bean 处理的bean 
	 */ 
	private void fieldAnnotation(Object bean) {
		try {
			//获取其全部的字段描述  
			// Field[] fields = bean.getClass().getFields(); // getField 只能获取public的，包括从父类继承来的字段。
			Field[] fields = bean.getClass().getDeclaredFields(); // getDeclaredField 可以获取本类所有的字段，包括private的，但是不能获取继承来的字段。
			for(Field f : fields){
				
				// 处理 Autowired 注解
				if(f!=null && f.isAnnotationPresent(Autowired.class)){
					Autowired resource = f.getAnnotation(Autowired.class);
					String name ="";  
					Object value = null;
					if(resource.required()){
						for(String key : sigletions.keySet()){
							//判断当前属性所属的类型是否在配置文件中存在 
							if(f.getType().isAssignableFrom(sigletions.get(key).getClass())){
								//获取类型匹配的实例对象  
								value = sigletions.get(key);
								break;
							}
						}
					}
					
					//允许访问private字段  
					f.setAccessible(true);  
					//把引用对象注入属性  
					f.set(bean, value);
					
				}
				
				// 处理 Value 注解
				if(f!=null && f.isAnnotationPresent(Value.class)){
					Value resource = f.getAnnotation(Value.class);
					String value = resource.value();
					f.setAccessible(true);
					if (Objects.equals(f.getType(),java.lang.Integer.class)) {
						f.set(bean, Integer.valueOf(value));
					}else { // 否则是字符串
						f.set(bean, value);
					}
					
				}
			}
		}catch(Exception e) {
			System.out.println("字段注解解析异常......");
		}
		
	}

	/** 
     * 处理在set方法上的注解 
     * 能在set方法上使用的注解都需要在此处理
     * @param bean 处理的bean 
     */
	private void propertyAnnotion(Object bean) {
		// 方法一
		try {
			Method[] methods = bean.getClass().getMethods();
			for(Method method : methods) {
				if(!Objects.equals(method.getAnnotations().length,0)) {
					Value resource = method.getAnnotation(Value.class);
					String name ="";
	                Object value = null;
	                if(resource.value()!=null&&!"".equals(resource.value())){ // 注意 Value.name无默认值
	                	//获取注解的name属性的内容  
	                    name = resource.value();  
	                    value = sigletions.get(name);
	                }else{
	                	for(String key : sigletions.keySet()){
	                		//判断当前属性所属的类型是否在配置文件中存在  
	                		Class<?>[] parameterTypes = method.getParameterTypes();
	                		for(Class classType : parameterTypes){
	                			if(Objects.equals(classType, sigletions.get(key).getClass())){
	                				System.out.println("class类型："+classType.toString());
	                				System.out.println("bean类型："+sigletions.get(key).getClass());
	                				//获取类型匹配的实例对象  
	                                value = sigletions.get(key);  
	                                break; 
	                			}
	                		}
	                	}
	                }
	                //允许访问private方法  
	                method.setAccessible(true);
	                //把引用对象注入属性  
	                method.invoke(bean, value);
	            }
			}
		}catch(Exception e) {
			System.out.println("方法注解解析释异常......");
		}
		/* 方法二
		try {
			PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
			for(PropertyDescriptor proderdesc : ps) {
				//获取所有set方法 
				Method setter = proderdesc.getWriteMethod();
				//判断set方法是否定义了注解  
                if(setter!=null && setter.isAnnotationPresent(ZxfResource.class)){
                	//获取当前注解，并判断name属性是否为空  
                    ZxfResource resource = setter.getAnnotation(ZxfResource.class);
                    String name ="";  
                    Object value = null;
                    if(resource.name()!=null&&!"".equals(resource.name())){
                    	//获取注解的name属性的内容  
                        name = resource.name();  
                        value = sigletions.get(name);
                    }else{
                    	for(String key : sigletions.keySet()){
                    		//判断当前属性所属的类型是否在配置文件中存在  
                            if(proderdesc.getPropertyType().isAssignableFrom(sigletions.get(key).getClass())){ 
                            	//获取类型匹配的实例对象  
                                value = sigletions.get(key);  
                                break; 
                            }
                    	}
                    }
                    //允许访问private方法  
                    setter.setAccessible(true);
                    //把引用对象注入属性  
                    setter.invoke(bean, value);
                }
			}
			
		}catch(Exception e) {
			System.out.println("属性注解解析释异常......");
		}
		*/
		
	}
	

	/** 
     * 获取Map中的对应的bean实例
     * @param beanId 
     * @return 
     */
	public Object getBean(String beanName) {
		
		return sigletions.get(beanName);
		
	}

}

